<?php

namespace app\admin\service\dev;

use app\admin\model\dev\Action as ActionModel;
use app\admin\model\dev\Field as FieldModel;
use app\admin\service\admin\I18nService;
use app\admin\service\generate\Generate;
use app\admin\validate\dev\Menu as MenuValidate;
use app\admin\model\dev\Menu as MenuModel;
use app\admin\model\dev\Api as ApiModel;
use app\common\Constants;
use app\common\exception\BusinessException;
use app\common\facade\HttpHelper;
use app\common\facade\StrHelper;
use app\common\helper\FileHelper;
use app\common\service\ApiClientService;
use Couchbase\BaseException;
use think\exception\ValidateException;
use think\facade\Db;
use think\facade\Log;

class MenuService
{

    /**
     * 获得菜单的权限前缀
     * @param $menu_id
     * @return mixed
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public static function getPermissionPrefix($menu_id): mixed
    {
        $menu_info = MenuModel::find($menu_id);
        $menu_names = StrHelper::getMenuNames($menu_info['controller']);
        $prefix = $menu_names['code_prefix'];
        return $prefix;
    }

    /**
     * 获得系统设置的数据库连接的字典列表
     * @return array
     */
    public static function getConnectionList(): array
    {
        $connects = [];
        foreach (config('database.connections') as $k => $v) {
            $connects[] = ['label' => strtoupper($k), 'value' => $k];
        }
        return $connects;
    }

    /**
     * 组织生成代码需要的数据
     * @param $menuId
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public static function prepareMenuData($menuId): array
    {
        // 准备菜单数据
        $menuInfo = Db::name('dev_menu')
            ->json(['config'])
            ->where('id', $menuId)
            ->find();
        // 是否存在表
        $menuInfo['table_exist'] = DatabaseService::hasTable($menuInfo['table_name'],$menuInfo['connect']) ? 1 : 0;
        // 准备字段数据
        $fieldList = Db::name('dev_field')
            ->json(['config'])
            ->where('menu_id', $menuId)
            ->where('status', 1)
            ->whereNull('delete_time')
            ->order('sort asc')
            ->select()->toArray();
        // 准备方法数据
        $actionList = Db::name('dev_action')
            ->json(['config'])
            ->where('menu_id', $menuId)
            ->where('status', 1)
            ->whereNull('delete_time')
            ->select()->toArray();
        $relationMenuList = self::getCascadeMenuInfo($actionList);
        return [
            'menuInfo' => $menuInfo,
            'fieldList' => $fieldList,
            'actionList' => $actionList,
            'relationMenuList' => $relationMenuList,
        ];
    }


    /**
     * 获得某个菜单所关联的菜单的信息
     * 比如：某菜单在index其他方法中定义了多表关联
     * 那么这里会返回这些关联的菜单对应数据
     * 方法是通过关联信息获得关联的表名，然通过表名查询到这些菜单的信息
     * @return array
     */
    private static function getCascadeMenuInfo($actionList): array
    {
        $configList = array_column($actionList, 'config');
        $relativeNames = [];
        foreach ($configList as $config) {
            if (!empty($config['with_join']) && is_array($config['with_join'])) {
                $names = array_column($config['with_join'], 'relative_name');
                $relativeNames = array_merge($relativeNames, $names);
            }
        }
        $relativeNames = array_unique($relativeNames);
        return Db::name('dev_menu')->where('table_name', "in", $relativeNames)->select()->toArray();
    }


    /**
     * 清理菜单确认
     * @return array
     */
    public static function cleanMenuConfirm(): array
    {
        $schema_name = config('database.connections.mysql.database');
        $menu_names = Db::name('dev_menu')->whereNotNull('delete_time')->column('name');
        $field_names = Db::name('dev_field')->whereNotNull('delete_time')->column('title');
        $action_names = Db::name('dev_action')->whereNotNull('delete_time')->column('title');
        $list = Db::query("SELECT TABLE_NAME FROM information_schema.tables WHERE TABLE_SCHEMA = '{$schema_name}' and TABLE_NAME REGEXP '_[0-9]{14}$'");
        trace($list, 'debug');
        $table_names = array_column($list, 'TABLE_NAME');
        $count = count($table_names) + count($menu_names) + count($field_names) + count($action_names);
        return [
            'count' => $count,
            'menu_names' => $menu_names,
            'field_names' => $field_names,
            'action_names' => $action_names,
            'table_names' => $table_names,
        ];
    }

    /**
     * 清理菜单
     * @return bool
     * @throws \think\db\exception\DbException
     */
    public static function cleanMenu(): bool
    {
        $schema_name = config('database.connections.mysql.database');
        // 彻底删除已标记为删除的菜单
        Db::name('dev_menu')->whereNotNull('delete_time')->delete(true);
        // 彻底删除已标记为删除的字段
        Db::name('dev_field')->whereNotNull('delete_time')->delete(true);
        // 彻底删除已标记为删除的方法
        Db::name('dev_action')->whereNotNull('delete_time')->delete(true);
        // 彻底删除已标记为备份的表
        $sql = "SELECT CONCAT('DROP TABLE `', table_name, '`;') AS delete_statement FROM information_schema.tables WHERE TABLE_SCHEMA = '{$schema_name}' and  table_name REGEXP '_[0-9]{14}$';";
        $result = Db::query($sql);
        if ($result) {
            foreach ($result as $sql) {
                Db::execute($sql['delete_statement']);
            }
        }
        // 为 dev_action dev_field dev_menu 表重置自增值
        $prefix = config('database.connections.mysql.prefix');
        if ($prefix) {
            $maxActionId = Db::name('dev_action')->max('id');
            $maxActionId += 1;
            Db::execute("ALTER TABLE {$prefix}dev_action AUTO_INCREMENT = {$maxActionId};");
            $maxFieldId = Db::name('dev_field')->max('id');
            $maxFieldId += 1;
            Db::execute("ALTER TABLE {$prefix}dev_field AUTO_INCREMENT = {$maxFieldId};");
            $maxMenuId = Db::name('dev_menu')->max('id');
            $maxMenuId += 1;
            Db::execute("ALTER TABLE {$prefix}dev_menu AUTO_INCREMENT = {$maxMenuId};");
        } else {
            throw new BaseException('请检查数据库链接名是否是mysql,或者检查数据库配置前缀是否设置');
        }

        return true;
    }

    /**
     * 复制菜单
     * @param array $post
     * @return void
     * @throws \Exception
     */
    public static function copyMenu(array $post): void
    {
        try {
            // 开始事务
            Db::startTrans();
            // 复制菜单
            $menu_info = Db::name('dev_menu')->findOrFail($post['id']);
            // 记录复制源
            $menu_info['from_id'] = $post['id'];
            // 根据复制到不同的应用分别处理
            if ($post['app'] == 'admin') {
                // 确定这个菜单是否被复制过？
                $copy_count = MenuModel::where([
                    'from_id' => $menu_info['id'],
                    'app' => 'admin',
                ])->count();
                // 拷贝n个,就有n+1个Ext (即使没有复制记录，也要加后缀)
                $camel_suffix = str_repeat('Ext', $copy_count + 1);
                $kebab_suffix = str_repeat('-ext', $copy_count + 1);
                $copy = self::addAdminSuffix($menu_info, $camel_suffix, $kebab_suffix);
                unset($copy['id']);
                $menuInfo = MenuModel::create($copy);
                // 复制方法
                $action_list = Db::name('dev_action')->where('menu_id', $post['id'])->select();
                foreach ($action_list as $action) {
                    unset($action['id']);
                    $action['menu_id'] = $menuInfo->id;
                    $action['url'] = null;
                    Db::name('dev_action')->insert($action);
                }
                // 复制字段
                $field_list = Db::name('dev_field')->where('menu_id', $post['id'])->select();
                foreach ($field_list as $field) {
                    unset($field['id']);
                    $field['menu_id'] = $menuInfo->id;
                    Db::name('dev_field')->insert($field);
                }
            }
            if ($post['app'] == 'api') {
                // 确定这个菜单是否被复制过？
                $copy_count = ApiModel::where([
                    'from_id' => $menu_info['id'],
                    'app' => 'api',
                ])->count();
                $copy = null;
                if ($copy_count > 0) {
                    $name_suffix = str_repeat('-复制', $copy_count + 1);
                    $camel_suffix = str_repeat('Ext', $copy_count + 1);
                    $copy = self::addApiSuffix($menu_info, $name_suffix, $camel_suffix);
                } else {
                    $name_suffix = str_repeat('-复制', 1);
                    $camel_suffix = str_repeat('Ext', 1);
                    $copy = self::addApiSuffix($menu_info, $name_suffix, $camel_suffix);
                }
                unset($copy['id']);
                $menuInfo = ApiModel::create($copy);
                // 复制方法
                $action_list = Db::name('dev_action')->where('menu_id', $post['id'])->select();
                foreach ($action_list as $action) {
                    unset($action['id']);
                    $action['menu_id'] = $menuInfo->id;
                    $action['app'] = 'api';
                    $action['gen_vue'] = 0;
                    $action['auth_jwt'] = 0;
                    $action['auth_sms'] = 0;
                    $action['auth_img'] = 0;
                    $action['code'] = null;
                    $action['key'] = null;
                    $action['url'] = $copy['url_prefix'] . '/' . $action['method'];
                    Db::name('dev_action')->insert($action);
                }
                // 复制字段
                $field_list = Db::name('dev_field')->where('menu_id', $post['id'])->select();
                foreach ($field_list as $field) {
                    unset($field['id']);
                    $field['menu_id'] = $menuInfo->id;
                    $field['key'] = null;
                    Db::name('dev_field')->insert($field);
                }
            }


            // 提交事务
            Db::commit();
        } catch (\Exception $e) {
            // 回退事务
            Db::rollback();
            throw $e;
        }
    }

    private static function addAdminSuffix($menu_info, $camel_suffix, $kebab_suffix)
    {
        $menu_info['gen_table'] = 0; // 不要生成表
        $menu_info['gen_code'] = 1; // 可以生成代码
        $menu_info['name'] = $menu_info['name'] . '(复制)';
        $menu_info['controller'] = $menu_info['controller'] . $camel_suffix;
        $menu_info['route_name'] = $menu_info['route_name'] . $camel_suffix;
        $menu_info['route_path'] = $menu_info['route_path'] . $kebab_suffix;
        $menu_info['route_path_full'] = $menu_info['route_path_full'] . $kebab_suffix;
        $menu_info['component'] = str_replace('/index', $kebab_suffix . '/index', $menu_info['component']);
        $menu_info['key'] = str_replace('.title', $kebab_suffix . '.title', $menu_info['key']);
        return $menu_info;
    }

    private static function addApiSuffix($menu_info, $name_suffix = null, $camel_suffix = null)
    {
        $new_controller = $camel_suffix ? $menu_info['controller'] . $camel_suffix : $menu_info['controller'];
        $menuNames = StrHelper::getMenuNames($new_controller);
        $menu_info['gen_table'] = 0; // 不要生成表
        $menu_info['gen_code'] = 1; // 可以生成代码
        $menu_info['name'] = $name_suffix ? $menu_info['name'] . $name_suffix : $menu_info['name'];
        $menu_info['controller'] = $new_controller;
        $menu_info['route_name'] = $menuNames['route_name'];
        $menu_info['route_path'] = $menuNames['route_path'];
        $menu_info['route_path_full'] = $menuNames['route_path_full'];
        $menu_info['component'] = null;
        $menu_info['url_prefix'] = $menuNames['url_prefix'];
        $menu_info['key'] = null;
        $menu_info['pid'] = 0;
        $menu_info['code'] = null;
        $menu_info['permission'] = 0;
        $menu_info['is_sys'] = 0;
        return $menu_info;
    }

    /**
     * 添加菜单
     * @param array $post
     * @return mixed
     * @throws \Exception
     */
    public static function addMenu(array $post): mixed
    {
        try {
            // 验证
            validate(MenuValidate::class)->scene('add')->check($post);
            // 检查菜单名是否已存在，避免重复创建
            if (MenuModel::where('controller', $post['controller'])->count() > 0) {
                throw new ValidateException('菜单已存在');
            }
            // 预处理菜单数据
            $menuData = self::transferMenuData($post);
            // 创建数据库表
            if ($menuData['type'] == Constants::MENU_TYPE_MENU && $post['gen_table'] == 1) {
                try {
                    if (DatabaseService::hasTable($menuData['table_name'], $menuData['connect'])) {
                        // 存在表则先重命名表，然后重新创建
                        DatabaseService::renameTable($menuData, date("YmdHis"));
                    }
                    DatabaseService::syncDatabaseTable($menuData);
                    $defaultFields = self::getDefaultCreateFields($menuData);
                    foreach ($defaultFields as $field) {
                        DatabaseService::createOrUpdateTableField($menuData, $menuData['primary_key'], $field);
                    }
                } catch (\Exception $e) {
                    DatabaseService::renameTable($menuData, date("YmdHis"));
                    throw $e;
                }
                $menuData['table_exist'] = Constants::MENU_TABLE_EXIST;
            }
            // 开启总事务
            Db::startTrans();
            $menuInfo = MenuModel::create($menuData);
            // 类型为菜单时
            if ($menuInfo['type'] === Constants::MENU_TYPE_MENU) {
                self::addDefaultFields($menuInfo->toArray());
                self::addDefaultActions($menuInfo->toArray());
                I18nService::asyncTranslationData($menuInfo['key'], $menuInfo['translations']);
            }
            // 提交总事务
            Db::commit();
            // 返回结果
            return $menuInfo->id;
        } catch (\Exception $e) {
            // 回退事务
            Db::rollback();
            // 删除创建的数据库表
            DatabaseService::renameTable($menuData, date("YmdHis"));
            throw $e;
        }
    }


    /**
     * 删除字段
     * @param array $post
     * @return true
     * @throws \Exception
     */
    public static function updateMenu(array $post): bool
    {
        try {
            // 验证
            validate(MenuValidate::class)->scene('update')->check($post);
            // 获取更新前的菜单信息
            $oldMenuInfo = MenuModel::findOrFail($post['id'])->toArray();
            // 预处理菜单数据
            $menuData = self::transferMenuData($post);
            // 同步数据库表结构（重命名表等）
            if ($post['gen_table'] === 1) {
                try {
                    DatabaseService::syncDatabaseTable($menuData, $oldMenuInfo);
                } catch (\Exception $e) {
                    DatabaseService::syncDatabaseTable($oldMenuInfo, $menuData);
                    throw $e;
                }
            }

            // 开启总事务
            Db::startTrans();
            // 更新菜单元数据
            MenuModel::update($menuData, ['id' => $menuData['id']]);
            // 如果主键发生变化，需要同步更新字段表中的记录
            if ($menuData['primary_key'] !== $oldMenuInfo['primary_key']) {
                self::updatePrimaryKeyField($menuData, $oldMenuInfo);
            }
            // 如果菜单的控制器名发生改变，则需要修改
            //1. 菜单对应的方法的权限代码
            //2. 菜单对应的字段的多语言键值
            // 注意与api的区别
            if ($menuData['controller'] !== $oldMenuInfo['controller']) {
                $oldMenuNames = StrHelper::getMenuNames($oldMenuInfo['controller']);
                $menuNames = StrHelper::getMenuNames($menuData['controller']);
                $actionList = ActionModel::where('menu_id', $menuData['id'])->select()->toArray();
                foreach ($actionList as $action) {
                    ActionModel::update([
                        'id' => $action['id'],
                        'code' => $menuNames['action_code_prefix'] . $action['method']
                    ]);
                    if (str_starts_with($action['key'], $oldMenuNames['lang_prefix'])) {
                        ActionModel::update([
                            'id' => $action['id'],
                            'key' => $menuNames['action_lang_prefix'] . $action['method']
                        ]);
                    }
                }
                $fieldList = FieldModel::where('menu_id', $menuData['id'])->select()->toArray();
                foreach ($fieldList as $field) {
                    FieldModel::update([
                        'id' => $field['id'],
                        'key' => $menuNames['field_lang_prefix'] . $field['field']
                    ]);
                }
            }
            // 更新翻译数据
            I18nService::asyncTranslationData($menuData['key'], $menuData['translations']);
            // 提交总事务
            Db::commit();
            // 返回结果
            return true;
        } catch (\Exception $e) {
            // 回退事务
            Db::rollback();
            // 还原成原来的配置
            DatabaseService::syncDatabaseTable($oldMenuInfo, $menuData);
            trace([
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'error_message' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ], 'debug');
            throw $e;
        }
    }


    /**
     * 删除字段
     * @param int $id
     * @return void
     * @throws \Exception
     */
    public static function deleteMenu(int $id): void
    {
        try {
            $menu_info = MenuModel::find($id);
            // 类型为菜单时
            if ($menu_info['type'] === Constants::MENU_TYPE_MENU) {
                // 此菜单如果创建了数据库表，则需要将表改名备份，改名失败需要恢复
                if ($menu_info['gen_table'] == 1) {
                    $timestamp = date("YmdHis");
                    try {
                        // 重命名表
                        DatabaseService::renameTable($menu_info, $timestamp);
                    } catch (\Exception $e) {
                        // 恢复重命名
                        DatabaseService::renameTable($menu_info, $timestamp, true);
                        throw new BusinessException(Constants::E_SYS_DATABASE_ERROR);
                    }
                }
            }
            // 开启事务
            Db::startTrans();
            // 类型为菜单时
            if ($menu_info['type'] === Constants::MENU_TYPE_MENU) {
                // 删除字段信息
                FieldService::deleteMenuFields($menu_info['id']);
                // 删除方法信息
                ActionService::deleteAction($menu_info['id']);
                // 删除语言信息
                I18nService::deleteTranslation($menu_info['controller']);
            }
            // 删除菜单信息
            MenuModel::destroy(['id' => $menu_info['id']]);
            // 提交事务
            Db::commit();
            // 删除相关文件
            self::deleteMenuFiles($menu_info);
        } catch (\Exception $e) {
            // 回退事务
            Db::rollback();
            // 恢复重命名
            DatabaseService::renameTable($menu_info, $timestamp, true);
            trace([
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'error_message' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ], 'debug');
            throw $e;
        }
    }


    /**
     * 获得菜单默认创建的字段信息
     * 根据这些信息向数据库的表中添加字段
     * 包含基于管理目的内置字段
     * @return array
     */
    public static function getDefaultCreateFields($menuInfo): array
    {
        try {
            $default_field_list = Db::name('dev_prop')
                ->json(['config'])
                ->where('type', '=', 'default_field')
                ->where('for_admin', '=', '1') // 后台菜单
                ->where('for_template', '=', $menuInfo['template']) // 综合表格模板
                ->column('config');
            // 获取内置字段
            $buildin_field_list = config('field');
            // 合并
            $field_list = array_merge($default_field_list, $buildin_field_list);
            // 调整顺序
            $first_key = array_key_first($field_list);
            $first_element = [$first_key => $field_list[$first_key]];
            $remaining_elements = array_slice($field_list, 1, null, true);
            $reversed_remaining = array_reverse($remaining_elements, true);
            $result_array = $first_element + $reversed_remaining;
            foreach ($result_array as $key => $value) {
                $default_create_fields[$key] = [
                    'field' => $value['field'],
                    'field_type' => $value['config']['field_type'],
                    'field_length' => $value['config']['field_length'],
                    'field_index' => $value['config']['field_index'],
                    'field_constraint' => $value['config']['field_constraint'],
                    'field_default' => $value['config']['field_default'],
                    'field_comment' => $value['title'],
                ];
            }
            trace(json_encode($default_create_fields), '默认创建的数据库表的字段');
            return $default_create_fields;
        } catch (\Exception $e) {
            trace([
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'error_message' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ], 'debug');
            throw $e;
        }
    }


    /**
     * 为菜单创建默认字段信息（dev_field）
     * @param $menu
     * @return true|void
     * @throws \Exception
     */
    public static function addDefaultFields($menu)
    {
        try {
            // 只有业务菜单才可以自动生成默认字段
            if ($menu['type'] === Constants::MENU_TYPE_MENU) {
                // 查询类型为【默认字段】，适用模板为【菜单模板】的所有默认菜单
                $default_field_list = Db::name('dev_prop')
                    ->json(['config'])
                    ->where('type', '=', 'default_field')
                    ->where('for_admin', '=', '1') // 后台菜单
                    ->where('for_template', '=', $menu['template']) // 综合表格模板 / 空白模板 / 表单模板
                    ->column('config');
                // 为字段设置所属菜单
                foreach ($default_field_list as &$field) {
                    $field["menu_id"] = $menu['id'];
                    // 内置的字段都有预设翻译数据
                    $field['key'] = 'common.field.' . $field['field'];
                    // 内置方法都设置为 buildin = 1
                    $field['buildin'] = 1;
                }
                // 解除引用
                unset($field);
                // 为菜单添加默认字段信息
                (new FieldModel())->saveAll($default_field_list);
                return true;
            }
        } catch (\Exception $e) {
            trace([
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'error_message' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ], 'debug');
            throw $e;
        }
    }


    /**
     * 添加默认字段
     * @param $menu
     * @return true|void
     * @throws \Exception
     */
    public static function addDefaultActions($menu)
    {
        try {
            // 只有业务菜单才可以自动生成默认字段
            if ($menu['type'] === Constants::MENU_TYPE_MENU) {
                $menu_names = StrHelper::getMenuNames($menu['controller']);
                // 查询类型为【默认方法】，适用模板为【菜单模板】的所有默认菜单
                $default_action_list = Db::name('dev_prop')
                    ->json(['config'])
                    ->where('type', '=', 'default_action')
                    ->where('for_admin', '=', '1') // 后台菜单
                    ->where('for_template', '=', $menu['template']) // 综合表格模板 / 空白模板 / 表单模板
                    ->column('config');
                foreach ($default_action_list as &$action) {
                    // 如果是表单模板，则在复制方法的时候，顺便把方法的类型改为 form
                    if ($menu['template'] === 'form' && $action['method'] === 'index') {
                        $action['type'] = 'form';
                    }
                    $action['app'] = 'admin';
                    $method = $action['method'];
                    $action["menu_id"] = $menu['id'];
                    $action['code'] = $menu_names['action_code_prefix'] . $method;
                    $action['url'] = null;
                    // 内置的方法都有预设翻译数据
                    $action['key'] = 'common.action.' . $method;
                    // 内置方法都设置为 buildin = 1
                    $action['buildin'] = 1;
                }
                unset($action); // 解除引用
                (new ActionModel())->saveAll($default_action_list);
                return true;
            }
        } catch (\Exception $e) {
            trace([
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'error_message' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ], 'debug');
            throw $e;
        }
    }


    /**
     * 将提交的数据格式化为可以直接存储到数据库的数据
     * @param $data
     * @return mixed
     * @throws \Exception
     */
    public static function transferMenuData($data)
    {
        try {
            $data['params'] = $data['params'] ? json_encode($data['params']) : null;
            // 类型为菜单
            if ($data['type'] === Constants::MENU_TYPE_MENU) {
                // 后端生成的命名规则为准
                $menu_names = StrHelper::getMenuNames($data['controller']);
                if ($menu_names['route_path_full'] != $data['route_path_full']) {
                    $data['route_path_full'] = $menu_names['route_path_full'];
                }
                if ($menu_names['route_name'] != $data['route_name']) {
                    $data['route_name'] = $menu_names['route_name'];
                }
                if ($menu_names['route_path'] != $data['route_path']) {
                    $data['route_path'] = $menu_names['route_path'];
                }
                if ($menu_names['component'] != $data['component']) {
                    $data['component'] = $menu_names['component'];
                }
                if ($menu_names['menu_lang_prefix'] . 'title' != $data['key']) {
                    $data['key'] = $menu_names['menu_lang_prefix'] . 'title';
                }
                if ($menu_names['action_code_prefix'] . '*' != $data['code']) {
                    $data['code'] = $menu_names['action_code_prefix'] . '*';
                }
                if ($menu_names['table_name'] != $data['table_name']) {
                    $data['table_name'] = $menu_names['table_name'];
                }
                $data['table_exist'] = Constants::MENU_TABLE_NOT_EXIST; // 默认未创建表
                $data['always_show'] = Constants::MENU_PARAMS_NO;
                $data['primary_key'] = $data['primary_key'] ?: 'id';
                $data['redirect'] = null;
                $data['is_sys'] = Constants::MENU_PARAMS_NO;
            }
            // 类型为目录
            if ($data['type'] === Constants::MENU_TYPE_CATALOG) {
                $data['keep_alive'] = Constants::MENU_PARAMS_NO;
                $data['connect'] = null;
                $data['table_name'] = null;
                $data['primary_key'] = null;
                $data['route_path_full'] = null;
                $data['route_name'] = null;
                $data['component'] = 'layout';
                $data['template'] = null;
                $data['params'] = null;
                $data['gen_table'] = Constants::MENU_PARAMS_NO;
                $data['gen_code'] = Constants::MENU_PARAMS_NO;
                // $data['redirect'] = null;
                $data['is_sys'] = Constants::MENU_PARAMS_NO;
                $data['code'] = null;
            }
            // 类型为外链
            if ($data['type'] === Constants::MENU_TYPE_EXTLINK) {
                $data['keep_alive'] = Constants::MENU_PARAMS_NO;
                $data['always_show'] = Constants::MENU_PARAMS_NO;
                $data['connect'] = null;
                $data['table_name'] = null;
                $data['primary_key'] = null;
                $data['component'] = null;
                $data['gen_table'] = Constants::MENU_PARAMS_NO;
                $data['gen_code'] = Constants::MENU_PARAMS_NO;
                $data['is_sys'] = Constants::MENU_PARAMS_NO;
            }
            // 类型为 IFRAME
            if ($data['type'] === Constants::MENU_TYPE_IFRAME) {
                $data['keep_alive'] = Constants::MENU_PARAMS_NO;
                $data['always_show'] = Constants::MENU_PARAMS_NO;
                $data['connect'] = null;
                $data['table_name'] = null;
                $data['primary_key'] = null;
                $data['route_path_full'] = '/' . preg_replace('#/index$#', '', $data['component']);
                $data['gen_table'] = Constants::MENU_PARAMS_NO;
                $data['gen_code'] = Constants::MENU_PARAMS_YES;
                $data['is_sys'] = Constants::MENU_PARAMS_NO;
            }
            return $data;
        } catch (\Exception $e) {
            trace([
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'error_message' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ], 'debug');
            throw $e;
        }
    }


    /**
     * 根据控制器名称转换成语言前缀
     * @param $controller
     * @return string
     * @throws \Exception
     * @example menu.demo.my.my-date
     */
    public static function convertToLanguagePrefix($controller): string
    {
        try {
            $parts = explode('/', $controller);
            $convertedParts = [];
            foreach ($parts as $part) {
                $converted = preg_replace('/(?<!^)([A-Z])/', '-$1', $part);
                $converted = strtolower($converted);
                $convertedParts[] = $converted;
            }
            $keyword = implode('.', $convertedParts);
            return 'menu.' . $keyword;
        } catch (\Exception $e) {
            trace([
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'error_message' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ], 'debug');
            throw $e;
        }
    }


    public static function genCodeHandle($menu_id, $overwrite = false): array
    {
        // 当前应用根目录
        $app_root_path = app()->getRootPath();
        // 准备菜单数据
        $menuData = MenuService::prepareMenuData($menu_id);
        if (!$menuData['menuInfo']['gen_code']) {
            throw new BusinessException('菜单不可生成代码');
        }
        $data = json_encode($menuData);
        $appId = config('rds.gen_code.app_id');
        $appSecret = config('rds.gen_code.app_secret');
        $baseUrl = config('rds.gen_code.base_url');
        $client = new ApiClientService($appId, $appSecret, $baseUrl);
        $path = '/api/basic.generater/gen';
        $response = $client->post($path, $data);
        if ($response['code'] != 200) {
            throw new BusinessException($response['message']);
        }
        // 写入文件
        foreach ($response['data'] as $code) {
            self::writeFile($app_root_path . $code['file'], $code['code'], $overwrite, $code['once']);
        }
        return [];
    }


    /******************************************************************************************************************/

    /**
     * 删除菜单文件
     * @param $menuInfo
     * @return true
     */
    private static function deleteMenuFiles($menuInfo): bool
    {
        $menuNames = StrHelper::getMenuNames($menuInfo['controller']);

        // 后端文件
        $app_root = app()->getRootPath();
        $app_path = $app_root . 'app' . DIRECTORY_SEPARATOR . 'admin' . DIRECTORY_SEPARATOR;

        $data['controller_base_file'] = $app_path . $menuNames['controller_base_path'];
        $data['controller_file'] = $app_path . $menuNames['controller_path'];

        $data['model_base_file'] = $app_path . $menuNames['model_base_path'];
        $data['model_file'] = $app_path . $menuNames['model_path'];

        $data['validate_base_file'] = $app_path . $menuNames['validate_base_path'];
        $data['validate_file'] = $app_path . $menuNames['validate_path'];

        // 前端文件和目录
        $app_root_path = app()->getRootPath();
        $jsapi_dir = implode(DIRECTORY_SEPARATOR, ['ui', 'src', 'api', $menuInfo['app']]);
        $data['jsapi_file'] = $app_root_path . $jsapi_dir . DIRECTORY_SEPARATOR . $menuNames['vue_jsapi'];

        $views = implode(DIRECTORY_SEPARATOR, ['ui', 'src', 'views', $menuInfo['app']]);
        $views_dir = $app_root_path . $views . DIRECTORY_SEPARATOR . $menuNames['views_dir'];

        // 删除文件
        foreach ($data as $file) {
            if (file_exists($file)) {
                unlink($file);
            }
        }

        // 删除目录下的文件
        if (is_dir($views_dir)) {
            FileHelper::deleteDirectoryRecursive($views_dir);
        }

        return true;
    }


    /**
     * 当主键变更时，同步更新 dev_field 表中的对应记录
     * @param array $newMenuInfo
     * @param array $oldMenuInfo
     */
    private static function updatePrimaryKeyField(array $newMenuInfo, array $oldMenuInfo): void
    {
        $fieldInfo = FieldModel::where('menu_id', $newMenuInfo['id'])
            ->where('field', $oldMenuInfo['primary_key'])
            ->find();
        if ($fieldInfo) {
            $menu_names = \app\common\facade\StrHelper::getMenuNames($newMenuInfo['controller']);
            $fieldInfo->field = $newMenuInfo['primary_key'];
            $fieldInfo->key = $menu_names['field_lang_prefix'] . $newMenuInfo['primary_key'];
            $fieldInfo->translations = ['zh-cn' => '编号', 'en' => 'ID'];
            $fieldInfo->save();
            // 同步多语言数据
            I18nService::asyncTranslationData($fieldInfo->key, $fieldInfo->translations);
        }
    }

    private static function writeFile($filepath, $content, $overwrite = true, $once = false): void
    {
        ob_start();
        echo $content;
        $_cache = ob_get_contents();
        ob_end_clean();

        $filepath = str_replace('/', DIRECTORY_SEPARATOR, $filepath);
        $filepath = str_replace('\\', DIRECTORY_SEPARATOR, $filepath);

        trace($filepath, 'WriteFile');

        if ($_cache) {
            // 如果文件存在
            if (file_exists($filepath)) {
                trace('文件已存在', 'WriteFile');
                // 不是仅生成一次
                if (!$once) {
                    trace('每次都生成', 'WriteFile');
                    // 需要备份旧文件
                    if (!$overwrite) {
                        trace('生成备份文件', 'WriteFile');
                        $backupFilepath = $filepath . date('YmdHis');
                        if (!rename($filepath, $backupFilepath)) {
                            trace('Failed to backup file: ' . $filepath . ' to ' . $backupFilepath, 'error');
                        }
                    }
                } else {
                    // 仅生成一次
                    trace('仅生成一次，跳过', 'WriteFile');
                    return;
                }
            }
            $file = new \think\template\driver\File();
            $file->write($filepath, $_cache);
        }
    }


}